package uabc.arduino.graficas;

import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import java.awt.Dimension;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;

import javax.swing.JOptionPane;
import javax.swing.JPanel;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;

import uabc.arduino.archivo.CsvManager;
import uabc.arduino.componentes.ChartConfiguration;
import uabc.arduino.componentes.ItemRendered;
import uabc.arduino.elementos.SerieConfiguration;
import uabc.arduino.enums.TipoGrafica;
import uabc.arduino.enums.TipoMonitoreo;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class CopyOfGrafica extends JPanel implements Runnable {

	private String titulo;
	private String unidades;
	private ChartConfiguration chartConf;
	private static Thread thread1;
	private XYPlot xyplot;
	private Generator generador;
	private ArrayList<SerieConfiguration> series;
	private TipoMonitoreo tipoMonitoreo;
	private SerialReader serialReader;
	private EthernetTCP ethernetTCP;
	private CsvManager csvManager;
	private String port;

	/**
	 * @wbp.parser.constructor
	 */
	public CopyOfGrafica(ChartConfiguration chartConf, TipoMonitoreo tipoMonitoreo,
			String port, CsvManager csvManager) {
		setBounds(100, 100, 630, 400);
		thread1 = new Thread(this);
		this.titulo = chartConf.getTitulo();
		this.unidades = chartConf.getUnidades();
		this.chartConf = chartConf;
		this.tipoMonitoreo = tipoMonitoreo;
		this.csvManager = csvManager;
		this.port = port;
		series = chartConf.getConfiguraciones();
		TimeSeriesCollection timeseriescollection = new TimeSeriesCollection();
		for (SerieConfiguration c : series) {
			@SuppressWarnings("deprecation")
			TimeSeries serie = new TimeSeries(c.getNombre(), Millisecond.class);
			c.setSerie(serie);
			timeseriescollection.addSeries(serie);
		}
		setLayout(null);
		ChartPanel chartpanel = new ChartPanel(
				createChart(timeseriescollection));
		chartpanel.setBounds(15, 30, 600, 350);
		chartpanel.setPreferredSize(new Dimension(600, 350));
		this.add(chartpanel);

		JButton btnPausePlay = new JButton("Pause/Play");
		btnPausePlay.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				pauseOrPlayGraph();
			}
		});
		btnPausePlay.setBounds(15, 0, 112, 23);
		add(btnPausePlay);

		JButton btnStop = new JButton("Stop");
		btnStop.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				stopGraph();
			}
		});
		btnStop.setBounds(127, 0, 61, 23);
		add(btnStop);

		for (int i = 0; i < series.size(); i++) {
			ItemRendered chckbxVisible = new ItemRendered(
					series.get(i).getID(), xyplot.getRenderer(), i);
			chckbxVisible.setBounds(15 + (i * 60), 377, 60, 25);
			add(chckbxVisible);
		}
		startThread();
	}

	public CopyOfGrafica(CsvManager csvManager) {
		setBounds(100, 100, 630, 400);
		this.titulo = "Titulo";
		this.unidades = "Unidades";
		ArrayList<String[]> data = csvManager.readerInfo();
		String[] encabezados = data.remove(0);
		int noSeries = encabezados.length - 1;
		TimeSeriesCollection timeseriescollection = new TimeSeriesCollection();
		TimeSeries[] series = new TimeSeries[noSeries];
		for (int i = 0; i < noSeries; i++) {
			series[i] = new TimeSeries(encabezados[i], Millisecond.class);
			timeseriescollection.addSeries(series[i]);
		}

		for (String[] campo : data) {
			for (int i = 0; i < noSeries; i++) {
				series[i].addOrUpdate(
						new Millisecond(new Date(campo[noSeries])), new Double(
								campo[i]));
			}
		}

		setLayout(null);
		ChartPanel chartpanel = new ChartPanel(
				createChart(timeseriescollection));
		chartpanel.setBounds(15, 30, 600, 350);
		chartpanel.setPreferredSize(new Dimension(600, 350));
		
		for (int i = 0; i < noSeries; i++) {
			ItemRendered chckbxVisible = new ItemRendered(
					encabezados[i], xyplot.getRenderer(), i);
			chckbxVisible.setBounds(15 + (i * 60), 377, 60, 25);
			add(chckbxVisible);
		}
		this.add(chartpanel);
	}

	private JFreeChart createChart(XYDataset dataset) {
		JFreeChart jfreechart = ChartFactory.createTimeSeriesChart(titulo,
				"hh:mm:ss", unidades, dataset, true, true, true);
		xyplot = jfreechart.getXYPlot();
		ValueAxis valueaxis = xyplot.getDomainAxis();
		if (chartConf != null)
			if (chartConf.getTipo() == TipoGrafica.OSCILOSCOPIO)
				valueaxis.setFixedAutoRange(chartConf.getPeriodo());
		valueaxis = xyplot.getRangeAxis();
		valueaxis.setAutoRange(true);
		return jfreechart;
	}

	private static class EthernetTCP extends Thread {
		private int port;
		private boolean running;
		private ServerSocket serverSocket;
		private BufferedReader in;
		private CsvManager csvManager;
		private boolean play;
		private ArrayList<SerieConfiguration> series;

		public void terminate() {
			running = false;
			csvManager.cerrar();
		}

		public void pauseOrPlay() {
			play = !play;
		}

		public void run() {
			try {
				serverSocket = new ServerSocket(port);
				Socket connectionSocket = serverSocket.accept();
				in = new BufferedReader(new InputStreamReader(
						connectionSocket.getInputStream()));
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			while (running)
				if (play) {
					try {
						String cadena = in.readLine();
						HashMap<String, Double> valores = decodificar(cadena);
						String[] csv = new String[series.size() + 1];
						int count = 0;
						for (SerieConfiguration s : series) {
							Millisecond millisecond = new Millisecond();
							double d = valores.get(s.getID());
							s.getSerie().add(millisecond, d);
							csv[count] = String.valueOf(d);
							count++;
						}
						csv[count] = (new Date().toString());
						csvManager.escribir(csv);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			csvManager.cerrar();
		}

		public HashMap<String, Double> decodificar(String cadena) {
			String[] entradas = cadena.split("-");
			HashMap<String, Double> mapa = new HashMap<String, Double>();
			for (int i = 0; i < entradas.length; i++) {
				String[] entrada = entradas[i].split(":");
				mapa.put(entrada[0], Double.parseDouble(entrada[1]));
			}
			return mapa;
		}

		private EthernetTCP(ArrayList<SerieConfiguration> series, int port,
				CsvManager csvManager) {
			this.series = series;
			this.port = port;
			this.csvManager = csvManager;
			running = true;
			this.play = true;
		}
	}

	public static class SerialReader extends Thread implements
			SerialPortEventListener {
		private SerialPort serialPort;
		private BufferedReader input;
		private String port;
		private static final int TIME_OUT = 2000;
		private static final int DATA_RATE = 9600;
		private boolean running;
		private CsvManager writer;
		private ArrayList<SerieConfiguration> series;
		private boolean play;

		public SerialReader(ArrayList<SerieConfiguration> series,
				CsvManager writer, String port) {
			this.series = series;
			this.running = true;
			this.writer = writer;
			this.port = port;
			this.play = true;
		}

		public void initialize() {
			CommPortIdentifier portId = null;
			Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();

			while (portEnum.hasMoreElements()) {
				CommPortIdentifier currPortId = (CommPortIdentifier) portEnum
						.nextElement();
				System.out.println(currPortId.getName());
				portId = currPortId;
				if (currPortId.getName().equals(port)) {
					portId = currPortId;
					break;
				}
			}
			if (portId == null) {
				JOptionPane.showMessageDialog(null, "No se encontro el puerto",
						"Puerto No Encontrado", JOptionPane.WARNING_MESSAGE);
				return;
			}

			try {
				serialPort = (SerialPort) portId.open(
						this.getClass().getName(), TIME_OUT);

				serialPort.setSerialPortParams(DATA_RATE,
						SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
						SerialPort.PARITY_NONE);

				input = new BufferedReader(new InputStreamReader(
						serialPort.getInputStream()));

				serialPort.addEventListener(this);
				serialPort.notifyOnDataAvailable(true);
			} catch (Exception e) {
				System.err.println(e.toString());
			}
		}

		public synchronized void close() {
			if (serialPort != null) {
				serialPort.removeEventListener();
				serialPort.close();
			}
		}

		public synchronized void serialEvent(SerialPortEvent oEvent) {
			if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
				if (play) {
					try {
						String cadena = input.readLine();
						HashMap<String, Double> valores = decodificar(cadena);
						String[] csv = new String[series.size() + 1];
						int count = 0;
						Millisecond millisecond = new Millisecond();
						for (SerieConfiguration s : series) {
							double d = valores.get(s.getID());
							s.getSerie().add(millisecond, d);
							csv[count] = String.valueOf(d);
							count++;
						}
						csv[count] = (new Date().toString());
						writer.escribir(csv);
					} catch (Exception e) {
						// System.err.println(e.toString());
					}
				}
			}
		}

		public HashMap<String, Double> decodificar(String cadena) {
			String[] entradas = cadena.split("-");
			HashMap<String, Double> mapa = new HashMap<String, Double>();
			for (int i = 0; i < entradas.length; i++) {
				String[] entrada = entradas[i].split(":");
				mapa.put(entrada[0], Double.parseDouble(entrada[1]));
			}
			return mapa;
		}

		public void terminate() {
			running = false;
			close();
			writer.cerrar();
		}

		public void pauseOrPlay() {
			play = !play;
		}

		public void run() {
			while (running) {
			}
			System.out.println("Termina");
		}
	}

	public void stopGraph() {
		if (serialReader != null)
			serialReader.terminate();
		if (ethernetTCP != null)
			ethernetTCP.terminate();
		if (generador != null)
			generador.terminate();
	}

	public void pauseOrPlayGraph() {
		if (serialReader != null) {
			serialReader.pauseOrPlay();
		} else if (ethernetTCP != null) {
			ethernetTCP.pauseOrPlay();
		} else if (generador != null) {
			generador.pauseOrPlay();
		}
	}

	public static void startThread() {
		thread1.start();
	}

	@Override
	public void run() {
		if (tipoMonitoreo == TipoMonitoreo.PuertoSerial) {
			serialReader = new SerialReader(series, csvManager, port);
			serialReader.initialize();
			serialReader.run();
		} else if (tipoMonitoreo == TipoMonitoreo.EthernetTCP) {
			ethernetTCP = new EthernetTCP(series, Integer.parseInt(port),
					csvManager);
			ethernetTCP.run();
		} else if (tipoMonitoreo == TipoMonitoreo.GENERADOR) {
			generador = new Generator(series, csvManager);
			generador.run();
		} else if (tipoMonitoreo == TipoMonitoreo.EthernetTCP) {

		}
	}

	public void terminate() {
		generador.running = false;
	}

	public String getUnidades() {
		return unidades;
	}

	public void setUnidades(String unidades) {
		this.unidades = unidades;
	}

	public ChartConfiguration getChartConf() {
		return chartConf;
	}

	public void setChartConf(ChartConfiguration chartConf) {
		this.chartConf = chartConf;
	}

	public CsvManager getCsvManager() {
		return csvManager;
	}

	public void setCsvManager(CsvManager csvManager) {
		this.csvManager = csvManager;
	}

	private static class Generator extends Thread {

		private ArrayList<SerieConfiguration> series;
		private double lastValue;
		private boolean play;
		private boolean running;
		private CsvManager csvManager;

		public void run() {
			while (running) {
				if (play) {
					try {
						String[] csv = new String[series.size() + 1];
						int count = 0;
						for (SerieConfiguration s : series) {
							double d = 0.9D + 0.2D * Math.random();
							lastValue = lastValue * d;
							Millisecond millisecond = new Millisecond();
							s.getSerie().add(millisecond, lastValue);
							csv[count] = lastValue + "";
							count++;
						}
						csv[count] = (new Date().toString());
						csvManager.escribir(csv);
						Thread.sleep(300);
					} catch (InterruptedException e) {

					}
				}
			}
		}

		public void terminate() {
			running = false;
			csvManager.cerrar();
		}

		public void pauseOrPlay() {
			play = !play;
		}

		private Generator(ArrayList<SerieConfiguration> series,
				CsvManager csvManager) {
			this.series = series;
			lastValue = 100D;
			running = true;
			play = true;
			this.csvManager = csvManager;
		}

	}
}
